/*
    alscache.js V0.1

   Copyright 2012 Matti Rinta-Nikkola

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

if (!String.prototype.trim) {
  String.prototype.trim = function () {
    return this.replace(/^\s+|\s+$/g, '');
  };
}

if (!String.prototype.splitNtrim) {
  String.prototype.splitNtrim = function (delim){
    var str = this.split(delim);
    var par = new Array();
    for (var i=0; i<str.length; i++){
      par[i] = str[i].trim();
    }
    return par;	
  };
}

// Declare namespace
alscache = window.alscache || {};

// Constructor for the alscache object
alscache.init = function () {
  var CACHE_CFG  = '/conf/alscache.cfg';
  var cacheOptions = {reqTimeout: 60,         // 1min
                      cfgMaxAge:  14400       // 4 h
                     };
  if (localStorage){
    cacheOptions.reqTimeout= localStorage.getItem('cache_reqTimeout');
    cacheOptions.cfgMaxAge = localStorage.getItem('cache_cfgMaxAge');
    cacheOptions.proxyUrl  = localStorage.getItem('cache_proxyUrl');
    cacheOptions.version   = localStorage.getItem('cache_version');
    if (cacheOptions.version) cacheOptions.version = 1*cacheOptions.version;
    if (!(cacheOptions.reqTimeout))cacheOptions.reqTimeout = 60;
    if (!(cacheOptions.cfgMaxAge)) cacheOptions.cfgMaxAge = 14400;  
  } else {
    var cfg = alscache.cfg2array(httpRequest(CACHE_CFG+"?"+currentTime));
    var p = cfg[0].splitNtrim(";");
    if (p[2]) cacheOptions.proxyUrl = p[2];
  }
  var rootPath   = alscache.rootPath();
  var wwwServer  = window.location.protocol + "//" + window.location.host;
  var fetchers   = [];
// 
// public methods
//
  var fetch = function (file, opt) {
    if (!opt) opt = {};
    try {   
      var f = doUrl(file);
      alscache.log ("fetching "+f.id);
      return load (f.url, f.id, opt);
    } catch (e){
      alscache.log ("fetch() exception...");
      alscache.log (e);
      if (opt.callback) opt.callback (null, opt.userdata);
      return null;
    }
  };

  var fetchCfg = function (file) {
    return alscache.cfg2array(fetch(file));
  };
//
// private methods
//
  var doUrl = function (file) {
    file = file.trim();
    var url = file;
    var d;
    if (file.substring(0,7) == "http://" || file.substring(0,8) == "https://") {
      d = file.search(/:/);
      if (window.location.host != file.substring (d+3, window.location.host.length+d+3)){
        if (cacheOptions.proxyUrl){
          url = wwwServer + cacheOptions.proxyUrl + file;
        } else {
          throw "ERROR: proxyUrl is null";
        }        
      }
    } else {
      url = wwwServer + file;
      if (file.substring(0,1) != "/") {
        url = wwwServer + rootPath + file;
      }
      file = url;
      d = file.search(/:/);
    } 
    return {url: url, 
            id:  file.substring(d+1, file.length) };
  };

  var saveParameters = function (now) {
    localStorage.setItem('cache_polling', now);  
    localStorage.setItem('cache_reqTimeout',cacheOptions.reqTimeout);
    localStorage.setItem('cache_cfgMaxAge', cacheOptions.cfgMaxAge);
    if(cacheOptions.version) localStorage.setItem('cache_version',   cacheOptions.version);   
    if(cacheOptions.proxyUrl)localStorage.setItem('cache_proxyUrl',  cacheOptions.proxyUrl);
  };

  var load = function (url, fileId, opt) {
    var currentTime = new Date();
    currentTime = currentTime.getTime();
    var response = null;
    if (localStorage){
      if (localStorage.getItem('cache_polling') == null ||
          currentTime > (1*localStorage.getItem('cache_polling') + 1000*localStorage.getItem('cache_cfgMaxAge'))){
        try {
          localStorage.setItem('cache_polling', currentTime);  
          var cfg = alscache.cfg2array(httpRequest(CACHE_CFG+"?"+currentTime));
          var ind = 0;
          var p = cfg[ind].splitNtrim(";");
          if (p[0] && alscache.isInteger(p[0])) cacheOptions.reqTimeout= p[0];
          if (p[1] && alscache.isInteger(p[1])) cacheOptions.cfgMaxAge = p[1];
          if (p[2]) cacheOptions.proxyUrl  = p[2];
          ind++;
          if (cfg[ind] && alscache.isInteger(cfg[ind]) ) {
            cacheOptions.version = 1*localStorage.getItem('cache_version')
            if (!cacheOptions.version || 1*cfg[ind] < 1 ||
                 cacheOptions.version < 1*cfg[ind]){
              localStorage.clear();
              cacheOptions.version = 1*cfg[ind];
            }
            ind++;
            for (var i = ind; i<cfg.length; i++){
              p = cfg[i].splitNtrim(";");
              if (p[1] && alscache.isInteger(p[1]) 
                        &&
                  (!localStorage.getItem('#'+p[0]) ||  1*p[1] < 1 ||
                  1*localStorage.getItem('#'+p[0]) < 1*p[1])){
                localStorage.removeItem(p[0]);             
                localStorage.setItem('#'+p[0], p[1]);  
              }
            }
          } else {
            alscache.log ("ERROR: Bad version number: "+ cfg[ind]);
            localStorage.clear(); 
          }
          saveParameters(currentTime);
        } catch (e) {
          alscache.log ("load() exception...");
          alscache.log (e);
          localStorage.clear(); 
        }
      }
      response = localStorage.getItem(fileId);
      if (!response){
        if ((localStorage.getItem('#'+fileId) && 1*localStorage.getItem('#'+fileId)==-1) || 
            (cacheOptions.version==-1 && !localStorage.getItem('#'+fileId))) fileId=null; 
        response = httpRequest (url+"?"+currentTime, fileId, opt);
      } else if (opt.callback) {
        opt.callback (response, opt.userdata);
      }
    } else {
      response = httpRequest (url+"?"+currentTime, null, opt);
    }
    alscache.log (response);    
    return response;
  };

  var getFetcher = function(){
    var xhrFetcher = new Object();
    if (!!fetchers.length) {
      xhrFetcher = fetchers.pop();
    } else {
      if (!!window.XMLHttpRequest) {
        xhrFetcher = new window.XMLHttpRequest(); // Most browsers
      } else if (!!window.ActiveXObject) {
        xhrFetcher = new window.ActiveXObject('Microsoft.XMLHTTP'); // Some IE
      }
    } 
    return xhrFetcher;  
  };
  var httpRequest = function (url, id, opt ){
    if (opt && opt.callback){
      return asyncro (url, id, opt);
    } else {
      return syncro (url, id);
    }
  };

  var syncro = function (U, id) {
    var X = getFetcher();
    X.open('GET', U, false );
    X.setRequestHeader('Content-Type', 'text/html')
    X.send('');
    if (X.status != 200){
      alscache.log ("XMLHttpRequest staus="+X.status);
      alscache.log (X.responseText);
      return null;
    }
    var rsp = X.responseText;
    if (id){
      try {
        localStorage.setItem(id, rsp); 
      } catch (e) {
        alscache.log ("syncro() exception...");
        alscache.log (e);
        localStorage.clear();
      }
    }
    fetchers.push(X);
    return rsp;
  };

  var asyncro = function (url, id, opt) {
    var X = getFetcher();
    function timeoutHandler() {
      alscache.log('request timed out...' + url);
      X.abort();
      opt.callback();
    };
    if (!X) {
      alscache.log('Unable to create XHR object');
      opt.callback(null);
    } else {
      if (X.overrideMimeType) {
        X.overrideMimeType('text/xml');
      }
      X.open('GET', url, true);
      X.onreadystatechange = function () {
        if (X.readyState === 4) {
          if (!!X._timeout)
            clearTimeout(X._timeout);
          if (X.status != 200) {
            alscache.log('HTTP error ' + X.status + ' retrieving ' + url);
            opt.callback();
          } else {
            alscache.log('requested...' + url);
            if (id){
              try {
                localStorage.setItem(id, X.responseText);
              } catch (e) {
                alscache.log ("asyncro() exception...");
                alscache.log (e);
                localStorage.clear();
              }
            }
            opt.callback(X.responseText, opt.userdata);
          }
          fetchers.push(X);
        }
      };
      X._timeout = setTimeout(timeoutHandler, cacheOptions.reqTimeout*1000);
      X.send(null);
    }
  };

  return {
    // Expose some properties and methods

    options: cacheOptions,
    
    fetch:    fetch,
    fetchCfg: fetchCfg

  };
};
// End of alscache cache

// Helper objects and functions

// Log a message to the debugging console, if one exists
alscache.log = function(msg) {
  if (!!window.console) {
    console.log(msg);
  }
};

alscache.isInteger = function (s) {
  return (s.toString().search(/^-?[0-9]+$/) == 0);
};

alscache.rootPath = function (){
   	var pathArray  = window.location.pathname.split( '/' );
   	var instanceRoot="";
   	var i;
   	for (i=0; i<pathArray.length-1; i++){ // first is "",  last is file name
   		if (pathArray[i].length > 0)
   			instanceRoot +=  '/' + pathArray[i]; 
   	}
   	return instanceRoot;
};

alscache.cfg2array = function (c){   
  var cfg = new Array;
  if (c != null){
    var cfgRaw = c.split(/\r?\n/g);
    for (var i = 0; i<cfgRaw.length; i++){
      var c = cfgRaw[i].trim();
      if (c != '' &&  c.substring(0,1) != "#"){
        cfg.push(c);
      }
    }
  }
  return cfg;
};

